<!-- **
    * @description search 搜索表单组件
** -->
<!-- **
    * @use 无需引入直接使用 <yfn-search :form="form" :data="data"></yfn-search>
    *      eg: <yfn-search :form="form" :extra-form="extraForm" :data="[{el: 'a-input', model: 'name', placeholder: '请输入'}, {el: 'yfn-input', model: 'name1', placeholder: '请输入'}]" @change="" @submit="submit"></yfn-search>
    * @params参数说明：
    * @params {form}                   表单数据绑定对象 Object
    * @params {data}                   表单项数据 Array
    * @params {extra-form}             表单数据绑定额外对象, [注：提取数组/选项类型的数据]
    * @params {size}                   用于控制该表单内组件的尺寸
    * @params {rules}                  表单验证规则
    * @params {label-align}            标签的对齐方向
    * @params {label-width}            标签宽度
    * @params {auto-label-width}       是否开启自动标签宽度，默认 false
    * @params {scroll-to-first-error}  验证失败后滚动到第一个错误字段，默认 false
    * @params {submit-text}            提交按钮文案，默认 搜索
    * @params {reset-text}             重置按钮文案，默认 重置
    * @params {has-submit}             是否显示搜索按钮，默认 false
    * @params {has-reset}              是否显示重置按钮，默认 true
    * @params {...}                    继承 a-form 原生属性/事件
** -->
<!-- **
    * @params {data}                   表单项数据
    * @params {data.el}                任意元素 eg: div、p、a-input、a-select、yfn-radio等
    * @params {data.model}             v-model 绑定字段
    * @params {data.modelSplit}        data.model 绑定字段值拆解, eg: model: ['2024-02-13', '2024-02-17']、modelSplit: ['firstTime', 'lastTime'] => form.firstTime = '2024-02-13'、form.lastTime = '2024-02-17'
    * @params {data.width}             el 宽度
    * @params {data.extraFormKey}      extra-form 额外绑定对象校验字段，默认 value
    * @params {data.on}                继承 el 元素原生事件，eg：input、focus、change、clear、click等
    * @params {data.children}          el 元素子级，参数同 data
    * @params {data.slotName}          el 元素插槽名称，继承 el 原生插槽，String/Array, eg：data: {slotName: 'prepend'} 或 data: {slotName: ['prepend', 'append', ...]}
    * @params {data.formItem}          a-form-item 数据配置
    * @params {...}                    继承 el 原生属性
** -->
<!-- **
    * @params {formItem}               el-form-item 数据配置
    * @params {formItem.label}         标签的文本
    * @params {formItem.field}         表单元素在数据对象中的path，默认为 data.model 值
    * @params {formItem.visible}       表单项是否显示，默认 true
    * @params {formItem.disabled}      表单项是否禁用，默认 false
    * @params {formItem.width}         表单项宽度
    * @params {formItem.label-width}   标签宽度
    * @params {formItem.show-colon}    表单项是否显示冒号，默认 false
    * @params {formItem.hide-label}    表单项是否显示冒号，默认 false
    * @params {formItem.merge-props}   表单项开启绑定，默认 true
    * @params {formItem.rules}         表单项校验规则（优先级高于 form 的 rules）
    * @params {formItem.slotName}      表单项插槽名称，String/Array, 同时继承 a-form-item 原生插槽
    * @params {formItem.label-col-style}         标签元素布局组件的 style
    * @params {formItem.wrapper-col-style}       表单控件布局组件的 style
    * @params {...}                    继承 a-form-item 原生属性
** -->
<!-- **
    * @events事件：
    * @change                          表单 change 事件
    * @submit                          提交
    * @reset                           重置
    * @get-instance                    获取表单组件实例
    * @{...}                           继承 a-form 原生事件
** -->
<!-- **
    * @Methods                         表单方法
    * @resetFields                     重置表单数据
    * @validate                        校验全部表单数据
    * @{...}                           继承 a-form 原生方法 Methods
** -->
<!-- **
    * @a-form slots插槽：
    * @slots {button-middle}          表单按钮中间插槽
    * @slots {button-form}            表单按钮插槽
    * @slots {...}                    自定义插槽
** -->
<!-- **
    * @a-form-item slots插槽：
    * @slots {label}                  表单项标签
    * @slots {help}                   表单项帮助信息
    * @slots {extra}                  表单项额外内容
    * @slots {...}                    自定义插槽
** -->
<template>
  <a-form class="YfnSearch" ref="formRef" v-bind="{ ...defaultOptions, ...attrs }" :model="props.form" v-if="biz.visible">
    <template v-for="(item, i) in props.data" :key="i">
      <a-form-item v-if="item.formItem?.visible ?? true" v-bind="setItemBind(item)">
        <!-- 表单项 -->
        <form-item :options="item" />
        <!-- slot -->
        <template v-for="list in setItemSlot(item)" :key="i + list" #[list]>
          <slot :name="list" :props="{ ...item, $index: i }"></slot>
        </template>
        <template v-for="list in setItemDefaultSlot(item)" :key="i + list">
          <slot :name="list" :props="{ ...item, $index: i }"></slot>
        </template>
      </a-form-item>
    </template>
    <!-- set -->
    <a-form-item :hide-label="true">
      <a-button v-if="props.hasSubmit" html-type="submit" type="primary">{{ props.submitText || '搜索' }}</a-button>
      <slot name="button-middle"></slot>
      <a-button v-if="props.hasReset" @click="handleReset">{{ props.resetText || '重置' }}</a-button>
      <slot name="button-form"></slot>
    </a-form-item>
  </a-form>
</template>

<script lang="ts" setup>
  import { ref, reactive, defineProps, useAttrs, useSlots, h, defineAsyncComponent, shallowRef, getCurrentInstance } from 'vue'
  /* interface */
  interface Biz {
    module: object
    compoModule: object
    visible: boolean
  }
  interface DefaultOptions {
    'auto-label-width': boolean
  }
  /* props */
  const props = defineProps({
    form: Object,
    data: Array,
    extraForm: Object,
    submitText: String,
    resetText: String,
    hasSubmit: Boolean,
    labelWidth: String,
    hasReset: {
      type: Boolean,
      default: () => {
        return true
      },
    },
  })
  /* proxy */
  const { proxy } = getCurrentInstance() as any
  /* emit */
  const emit = defineEmits(['change', 'reset', 'get-instance'])
  /* attrs */
  const attrs = useAttrs()
  /* slots */
  const slots = useSlots()
  /* data */
  const formRef = ref(null)
  const biz: Biz = reactive({
    module: {},
    compoModule: {},
    visible: false,
  })
  const defaultOptions: DefaultOptions = reactive({
    'auto-label-width': true,
  })
  /* fn */
  // bind
  const setItemBind = (obj: any) => {
    const labelKey = 'label-col-style'
    const wrapperKey = 'wrapper-col-style'
    const formItem = obj.formItem || {}
    const labelWidth = formItem['label-width'] || props.labelWidth
    const wrapperWidth = formItem['width']
    let dv = {}
    if (obj.model) {
      dv['field'] = formItem.field ?? obj.model
      dv['merge-props'] = formItem['merge-props'] ?? true
    }
    dv['hide-label'] = formItem['hide-label'] ?? !(formItem.label || (formItem.slotName || []).includes('label'))
    // style 处理
    // label 标签
    const labelColStyle = {
      flex: 'none',
      width: labelWidth,
      ...formItem[labelKey],
    }
    labelWidth && (labelColStyle.flex = `0 0 ${labelWidth}`)
    // wrapper 控件
    let wrapperColStyle = {
      width: wrapperWidth,
      ...formItem[wrapperKey],
    }
    wrapperWidth && (wrapperColStyle.flex = `0 0 ${wrapperWidth}`)
    const bind = {
      ...dv,
      ...formItem,
      [labelKey]: labelColStyle,
      [wrapperKey]: wrapperColStyle,
    }
    return bind
  }
  // slot
  const getSlot = (obj: any) => {
    const slotName = obj.formItem?.slotName
    const hasSlotString = Object.prototype.toString.call(slotName) == '[object String]'
    const slotArr = hasSlotString ? [slotName] : slotName
    return slotArr || []
  }
  const setItemSlot = (obj: any) => {
    return getSlot(obj)
  }
  const setItemDefaultSlot = (obj: any) => {
    const nameSlot = ['label', 'help', 'extra']
    const arr = getSlot(obj).filter((p: any) => {
      return !nameSlot.includes(p)
    })
    return arr
  }
  // 额外data
  const setExtraModel = (obj: any) => {
    const extraObj = obj || {}
    const hasTrue = !!extraObj.model
    const list = extraObj.options || extraObj.children || []
    let arr: any = []
    list.forEach((p: any) => {
      if (p.children) {
        arr.push(...p.children)
      } else {
        arr.push(p)
      }
    })
    if (!hasTrue) return
    // 额外选项值
    const modelValue = props.form[extraObj.model]
    const extraOptions = () => {
      const key = extraObj.extraFormKey || 'value'
      const arrFilter = arr.filter((p: any) => {
        return p[key] == modelValue
      })
      const first = arrFilter[0]
      if (first && props.extraForm) {
        props.extraForm[extraObj.model] = first
      }
    }
    extraOptions()
    // model拆解
    const modelSplitFn = () => {
      if (!extraObj.modelSplit) return
      extraObj.modelSplit.forEach((p: string, i: number) => {
        props.form[p] = (modelValue || [])[i]
      })
    }
    modelSplitFn()
  }
  // update
  const setUpdate = (obj: any) => {
    emit('change', { form: props.form, options: obj, proxy: proxy })
  }
  // 表单操作
  const handleReset = () => {
    // 重置
    proxy.$refs.formRef.resetFields()
    emit('reset', { form: props.form })
  }
  // 获取文件
  const getLocalFile = () => {
    const compo = import.meta.glob('./**/*.vue', { eager: true })
    const compoModule: object = {}
    Object.keys(compo).forEach((key: string) => {
      const name = (compo as any)[key].default.__name
      compoModule[name as string] = shallowRef((compo as any)[key].default)
    })
    biz.compoModule = compoModule
  }
  const getFile = () => {
    import(`@arco-design/web-vue/es`).then((res) => {
      biz.module = res
      biz.visible = true
      emit('get-instance', proxy)
    })
  }
  getLocalFile()
  getFile()
  // key值
  const getKey = (key: any) => {
    const keyAttr = key.includes('a-') ? key.split('a-')[1] : key
    let keyR = ''
    if (keyAttr) {
      const keyArr = keyAttr.split('-')
      keyArr.forEach((p: string) => {
        const str = `${p.charAt(0).toUpperCase()}${p.slice(1)}`
        keyR += str
      })
    }
    return keyR
  }
  // key-delete
  const deleteKey = (obj = {}, arr = []) => {
    let v = {}
    Object.keys(obj).forEach((key: any) => {
      !arr.includes(key) && (v[key] = obj[key])
    })
    return v
  }
  /* render */
  const FormItem = defineAsyncComponent(() => {
    return new Promise((resolve) => {
      const createEl: any = renderFn()
      resolve(createEl)
    })
  })
  const renderFn = () => {
    return {
      props: {
        options: Object,
      },
      render(): any {
        const defaultDeleteBind: any = ['el', 'formItem', 'formLabel', 'slot', 'slotName', 'on', 'children', 'modelSplit']
        const createNode = (options: any) => {
          const formItemOptions = options.formItem || {}
          // 元素
          const elKey = getKey(options.el)
          const el = biz.module[elKey] || biz.compoModule[elKey] || options.el
          // 事件
          let on = {}
          if (options.on) {
            Object.keys(options.on).forEach((key) => {
              const str = `on${key.charAt(0).toUpperCase()}${key.slice(1)}`
              on[str] = options.on[key]
            })
          }
          // 属性
          const defaultAttrs = options.model ? { 'allow-clear': true, style: {} } : { style: {} }
          const attrsKey = deleteKey(options, defaultDeleteBind)
          let attrs: any = {
            ...on,
            ...defaultAttrs,
            ...attrsKey,
          }
          if (options.width) {
            attrs.style.width = options.width
          }
          // v-model
          if (options.model) {
            attrs.modelValue = props.form[options.model]
            attrs['onUpdate:modelValue'] = (e: any) => {
              props.form[options.model] = e
              setUpdate(options)
              setExtraModel(options)
            }
            setExtraModel(options)
          }
          // 子元素
          let childNodes: any = null
          if (options.children) {
            const mergeProps = formItemOptions['merge-props'] === false
            const childNodesArr = options.children.map((p: any) => {
              let child: any = ''
              const pFormItem = p.formItem || {}
              if (mergeProps) {
                // 子元素-动态
                child = h(
                  biz.module['FormItem'],
                  {
                    'hide-label': pFormItem['hide-label'] ?? !pFormItem.label,
                    field: p.model,
                    ...p.formItem,
                  },
                  { default: () => createNode(p) }
                )
              } else {
                // 子元素-静态
                child = createNode(p)
              }
              return child
            })
            childNodes = { default: () => childNodesArr }
          }
          // 选项
          if (['a-radio'].includes(options.el)) {
            childNodes = { default: () => options.label || options.value }
          }
          // slot
          if (options.slotName) {
            childNodes = {}
            const hasSlotString = Object.prototype.toString.call(options.slotName) == '[object String]'
            const slotArr = hasSlotString ? [options.slotName] : options.slotName
            slotArr.forEach((p: any) => {
              slots[p] && (childNodes[p] = slots[p])
            })
          }
          return h(el, attrs, childNodes)
        }
        return createNode(this.options)
      },
    }
  }
</script>

<style scoped lang="less">
  .YfnSearch {
    flex-direction: initial;
    flex-wrap: wrap;
    background: #fff;
    padding: 10px 0 0 10px;
    box-sizing: border-box;
  }
  :deep(.arco-form-item) {
    width: auto;
    margin: 0 8px 10px 0;
    .arco-form-item {
      margin: 0;
    }
  }
  :deep(.arco-btn) {
    &:not(:last-child) {
      margin-right: 8px;
    }
  }
</style>
